home *** CD-ROM | disk | FTP | other *** search
/ Aminet 16 / Aminet 16 (1996)(GTI - Schatztruhe)[!][Dec 1996].iso / Aminet / comm / term / term_source.lha / Extras / Source / term-source.lha / Accountant.c < prev    next >
C/C++ Source or Header  |  1996-10-21  |  11KB  |  517 lines

  1. /*
  2. **    Accountant.c
  3. **
  4. **    Connection cost accounting
  5. **
  6. **    Copyright © 1990-1996 by Olaf `Olsen' Barthel
  7. **        All Rights Reserved
  8. **
  9. **    :ts=4
  10. */
  11.  
  12. #ifndef _GLOBAL_H
  13. #include "Global.h"
  14. #endif
  15.  
  16. /****************************************************************************/
  17.  
  18. enum    { STATE_Idle, STATE_Waiting };
  19.  
  20. /****************************************************************************/
  21.  
  22. enum    { ACMSG_Start,ACMSG_Stop,ACMSG_Query };
  23.  
  24. typedef struct AccountantMsg
  25. {
  26.     struct Message    Message;
  27.  
  28.     UWORD            Type;
  29.     struct timeval    Time;
  30. } AccountantMsg;
  31.  
  32. /****************************************************************************/
  33.  
  34. STATIC struct MsgPort            *AccountantPort;
  35. STATIC struct Task                *AccountantTask;
  36.  
  37. STATIC struct SignalSemaphore     AccountantSemaphore;
  38. STATIC ULONG                     AccountantCost;
  39.  
  40. /****************************************************************************/
  41.  
  42.     /* AccountantEntry(VOID):
  43.      *
  44.      *    The rates accounting task.
  45.      */
  46.  
  47. STATIC VOID SAVE_DS
  48. AccountantEntry(VOID)
  49. {
  50.     struct MsgPort *TimePort;
  51.  
  52.         /* Set up the timer resources */
  53.  
  54.     if(TimePort = CreateMsgPort())
  55.     {
  56.         struct timerequest *TimeRequest;
  57.  
  58.         if(TimeRequest = (struct timerequest *)CreateIORequest(TimePort,sizeof(struct timerequest)))
  59.         {
  60.             if(!OpenDevice(TIMERNAME,UNIT_VBLANK,(struct IORequest *)TimeRequest,NULL))
  61.             {
  62.                     /* Now create the communications port */
  63.  
  64.                 if(AccountantPort = CreateMsgPort())
  65.                 {
  66.                     struct timeval StopTime;
  67.                     struct timeval Now;
  68.                     ULONG Signals;
  69.                     WORD State;
  70.  
  71.                         /* That's me */
  72.  
  73.                     AccountantTask = FindTask(NULL);
  74.  
  75.                         /* Wake up the main program */
  76.  
  77.                     Signal((struct Task *)ThisProcess,SIG_HANDSHAKE);
  78.  
  79.                         /* Doing nothing yet... */
  80.  
  81.                     State = STATE_Idle;
  82.  
  83.                     FOREVER
  84.                     {
  85.                             /* Wait for a sign */
  86.  
  87.                         Signals = Wait(PORTMASK(TimePort) | PORTMASK(AccountantPort) | SIG_KILL);
  88.  
  89.                             /* Terminate this task? */
  90.  
  91.                         if(Signals & SIG_KILL)
  92.                             break;
  93.  
  94.                             /* The timer has elapsed? */
  95.  
  96.                         if((Signals & PORTMASK(TimePort)) && State == STATE_Waiting)
  97.                         {
  98.                             PhoneEntry *ChosenEntry;
  99.                             struct List *ChosenPattern;
  100.                             ULONG Cost;
  101.  
  102.                                 /* Terminate the timer request */
  103.  
  104.                             WaitIO((struct IORequest *)TimeRequest);
  105.  
  106.                                 /* We are no longer waiting */
  107.  
  108.                             State = STATE_Idle;
  109.  
  110.                                 /* Start with a clean slate */
  111.  
  112.                             Cost = 0;
  113.  
  114.                                 /* We want to read the accounting data */
  115.  
  116.                             ChosenPattern = LockActivePattern();
  117.                             ChosenEntry = LockActiveEntry(GlobalPhoneHandle);
  118.  
  119.                             if(ChosenEntry || ChosenPattern)
  120.                             {
  121.                                     /* Look up the current time */
  122.  
  123.                                 GetSysTime(&Now);
  124.  
  125.                                     /* Check which rate data is current */
  126.  
  127.                                 SelectTime(ChosenEntry,ChosenPattern,&Now);
  128.  
  129.                                     /* Store the cost for the next unit */
  130.  
  131.                                 Cost = PayPerUnit[DT_NEXT_UNIT];
  132.  
  133.                                     /* Does this unit last for a certain time? */
  134.  
  135.                                 if(SecPerUnit[DT_NEXT_UNIT] > 0)
  136.                                 {
  137.                                     struct timeval TimeVal;
  138.  
  139.                                         /* This is the time it takes for the */
  140.                                         /* unit to elapse */
  141.  
  142.                                     TimeVal.tv_secs        = SecPerUnit[DT_NEXT_UNIT] / 10000;
  143.                                     TimeVal.tv_micro    = (SecPerUnit[DT_NEXT_UNIT] % 10000) * 100;
  144.  
  145.                                         /* Remember when the unit will have elapsed */
  146.  
  147.                                     GetSysTime(&StopTime);
  148.                                     AddTime(&StopTime,&TimeVal);
  149.  
  150.                                         /* Start waiting again */
  151.  
  152.                                     TimeRequest->tr_node.io_Command    = TR_ADDREQUEST;
  153.                                     TimeRequest->tr_time            = TimeVal;
  154.  
  155.                                     SendIO((struct IORequest *)TimeRequest);
  156.  
  157.                                     State = STATE_Waiting;
  158.                                 }
  159.                             }
  160.  
  161.                             UnlockActiveEntry(GlobalPhoneHandle);
  162.                             UnlockActivePattern();
  163.  
  164.                                 /* Add up the connection cost */
  165.  
  166.                             ObtainSemaphore(&AccountantSemaphore);
  167.                             AccountantCost += Cost;
  168.                             ReleaseSemaphore(&AccountantSemaphore);
  169.                         }
  170.  
  171.                             /* Somebody wants us to do something... */
  172.  
  173.                         if(Signals & PORTMASK(AccountantPort))
  174.                         {
  175.                             AccountantMsg *Message;
  176.  
  177.                                 /* For each message... */
  178.  
  179.                             while(Message = (AccountantMsg *)GetMsg(AccountantPort))
  180.                             {
  181.                                 switch(Message->Type)
  182.                                 {
  183.                                         /* Start waiting? */
  184.  
  185.                                     case ACMSG_Start:
  186.  
  187.                                             /* Remember the time when this unit */
  188.                                             /* will have elapsed */
  189.  
  190.                                         GetSysTime(&StopTime);
  191.                                         AddTime(&StopTime,&Message->Time);
  192.  
  193.                                             /* Start waiting */
  194.  
  195.                                         TimeRequest->tr_node.io_Command    = TR_ADDREQUEST;
  196.                                         TimeRequest->tr_time            = Message->Time;
  197.  
  198.                                         SendIO((struct IORequest *)TimeRequest);
  199.  
  200.                                         State = STATE_Waiting;
  201.  
  202.                                         break;
  203.  
  204.                                         /* Stop waiting? */
  205.  
  206.                                     case ACMSG_Stop:
  207.  
  208.                                             /* Are we currently waiting? */
  209.  
  210.                                         if(State == STATE_Waiting)
  211.                                         {
  212.                                             if(!CheckIO((struct IORequest *)TimeRequest))
  213.                                                 AbortIO((struct IORequest *)TimeRequest);
  214.  
  215.                                             WaitIO((struct IORequest *)TimeRequest);
  216.  
  217.                                             State = STATE_Idle;
  218.                                         }
  219.  
  220.                                         break;
  221.  
  222.                                         /* Return the time that still has to elapse? */
  223.  
  224.                                     case ACMSG_Query:
  225.  
  226.                                         if(State == STATE_Waiting)
  227.                                         {
  228.                                                 /* This is now */
  229.  
  230.                                             GetSysTime(&Now);
  231.  
  232.                                                 /* Is there still time left */
  233.                                                 /* before the unit elapses? */
  234.  
  235.                                             if(CmpTime(&StopTime,&Now) < 0)    /* i.e. "StopTime > Now" */
  236.                                             {
  237.                                                     /* Return the remaining time */
  238.  
  239.                                                 Message->Time = StopTime;
  240.  
  241.                                                 SubTime(&Message->Time,&Now);
  242.  
  243.                                                 break;
  244.                                             }
  245.                                         }
  246.  
  247.                                             /* Clear the time value */
  248.  
  249.                                         memset(&Message->Time,0,sizeof(Message->Time));
  250.  
  251.                                         break;
  252.                                 }
  253.  
  254.                                     /* Return the message */
  255.  
  256.                                 ReplyMsg((struct Message *)Message);
  257.                             }
  258.                         }
  259.                     }
  260.  
  261.                         /* Are we still waiting? */
  262.  
  263.                     if(State == STATE_Waiting)
  264.                     {
  265.                         if(!CheckIO((struct IORequest *)TimeRequest))
  266.                             AbortIO((struct IORequest *)TimeRequest);
  267.  
  268.                         WaitIO((struct IORequest *)TimeRequest);
  269.                     }
  270.  
  271.                         /* Start cleaning up... */
  272.  
  273.                     DeleteMsgPort(AccountantPort);
  274.                 }
  275.  
  276.                 CloseDevice((struct IORequest *)TimeRequest);
  277.             }
  278.  
  279.             DeleteIORequest((struct IORequest *)TimeRequest);
  280.         }
  281.  
  282.         DeleteMsgPort(TimePort);
  283.     }
  284.  
  285.         /* Finished; wave goodbye and exit */
  286.  
  287.     Forbid();
  288.  
  289.     AccountantTask = NULL;
  290.     Signal((struct Task *)ThisProcess,SIG_HANDSHAKE);
  291. }
  292.  
  293. /****************************************************************************/
  294.  
  295.     /* DeleteAccountant():
  296.      *
  297.      *    Stop the accounting task.
  298.      */
  299.  
  300. VOID
  301. DeleteAccountant()
  302. {
  303.     ShakeHands(AccountantTask,SIG_KILL);
  304. }
  305.  
  306.     /* CreateAccountant():
  307.      *
  308.      *    Launch the accounting task.
  309.      */
  310.  
  311. BOOL
  312. CreateAccountant()
  313. {
  314.     InitSemaphore(&AccountantSemaphore);
  315.  
  316.     Forbid();
  317.  
  318.     if(LocalCreateTask("term Accountant Task",ThisProcess->pr_Task.tc_Node.ln_Pri + 10,(TASKENTRY)AccountantEntry,4096,0))
  319.         WaitForHandshake();
  320.  
  321.     Permit();
  322.  
  323.     return((BOOL)(AccountantTask != NULL));
  324. }
  325.  
  326. /****************************************************************************/
  327.  
  328.     /* QueryAccountantTime(struct timeval *Time):
  329.      *
  330.      *    Query the time that will have to elapse before
  331.      *    the next unit will begin.
  332.      */
  333.  
  334. ULONG
  335. QueryAccountantTime(struct timeval *Time)
  336. {
  337.     AccountantMsg Message;
  338.  
  339.     memset(&Message,0,sizeof(Message));
  340.  
  341.     Message.Message.mn_Length    = sizeof(Message);
  342.     Message.Type                = ACMSG_Query;
  343.  
  344.     SendMessageGetReply(AccountantPort,(struct Message *)&Message);
  345.  
  346.     if(Time)
  347.     {
  348.         Time->tv_secs    = Message.Time.tv_secs;
  349.         Time->tv_micro    = Message.Time.tv_micro;
  350.     }
  351.  
  352.     return(Message.Time.tv_secs);
  353. }
  354.  
  355.     /* QueryAccountantCost():
  356.      *
  357.      *    Query the cost of the current connection.
  358.      */
  359.  
  360. ULONG
  361. QueryAccountantCost()
  362. {
  363.     ULONG Cost;
  364.  
  365.     SafeObtainSemaphoreShared(&AccountantSemaphore);
  366.     Cost = AccountantCost;
  367.     ReleaseSemaphore(&AccountantSemaphore);
  368.  
  369.     return(Cost);
  370. }
  371.  
  372.     /* StopAccountant():
  373.      *
  374.      *    Stop cost accounting and return the current
  375.      *    connection cost.
  376.      */
  377.  
  378. ULONG
  379. StopAccountant()
  380. {
  381.     AccountantMsg Message;
  382.     ULONG Cost;
  383.  
  384.     memset(&Message,0,sizeof(Message));
  385.  
  386.     Message.Message.mn_Length    = sizeof(Message);
  387.     Message.Type                = ACMSG_Stop;
  388.  
  389.     SendMessageGetReply(AccountantPort,(struct Message *)&Message);
  390.  
  391.     SafeObtainSemaphoreShared(&AccountantSemaphore);
  392.     Cost = AccountantCost;
  393.     ReleaseSemaphore(&AccountantSemaphore);
  394.  
  395.     return(Cost);
  396. }
  397.  
  398.     /* StartAccountant(ULONG OnlineSeconds):
  399.      *
  400.      *    Start cost accounting for the current connection.
  401.      */
  402.  
  403. VOID
  404. StartAccountant(ULONG OnlineSeconds)
  405. {
  406.     PhoneEntry *ChosenEntry;
  407.     struct List *ChosenPattern;
  408.     struct timeval Remain;
  409.     struct timeval Now;
  410.     ULONG Cost;
  411.  
  412.     ChosenPattern = LockActivePattern();
  413.     ChosenEntry = LockActiveEntry(GlobalPhoneHandle);
  414.  
  415.         /* Forget the old data */
  416.  
  417.     StopAccountant();
  418.  
  419.         /* Now is the time for all good men... */
  420.  
  421.     GetSysTime(&Now);
  422.  
  423.         /* Did the connection happen a few seconds earlier? */
  424.  
  425.     if(OnlineSeconds > 0)
  426.     {
  427.         struct timeval Lead;
  428.  
  429.             /* Take care of the leading time */
  430.  
  431.         Lead.tv_secs    = OnlineSeconds;
  432.         Lead.tv_micro    = 0;
  433.  
  434.             /* Technically, the connection happened a bit earlier. */
  435.  
  436.         SubTime(&Now,&Lead);
  437.  
  438.             /* Choose the right rate accounting time. */
  439.  
  440.         SelectTime(ChosenEntry,ChosenPattern,&Now);
  441.  
  442.             /* Enter the first rate. */
  443.  
  444.         Cost = PayPerUnit[DT_FIRST_UNIT];
  445.  
  446.         Remain.tv_secs    = SecPerUnit[DT_FIRST_UNIT] / 10000;
  447.         Remain.tv_micro    = (SecPerUnit[DT_FIRST_UNIT] % 10000) * 100;
  448.  
  449.             /* Did the first unit elapse already? */
  450.  
  451.         while(-CmpTime(&Lead,&Remain) >= 0 && (Remain.tv_secs || Remain.tv_micro))    /* "Lead >= Remain" */
  452.         {
  453.                 /* Subtract the unit time */
  454.  
  455.             SubTime(&Lead,&Remain);
  456.  
  457.                 /* Update the "current" time so SelectTime */
  458.                 /* can do something sensible. */
  459.  
  460.             AddTime(&Now,&Remain);
  461.  
  462.             SelectTime(ChosenEntry,ChosenPattern,&Now);
  463.  
  464.             Remain.tv_secs    = SecPerUnit[DT_NEXT_UNIT] / 10000;
  465.             Remain.tv_micro    = (SecPerUnit[DT_NEXT_UNIT] % 10000) * 100;
  466.  
  467.                 /* In any case, a new unit starts here */
  468.  
  469.             Cost += PayPerUnit[DT_NEXT_UNIT];
  470.         }
  471.  
  472.             /* Adjust the remaining time it takes for the unit */
  473.             /* to elapse. */
  474.  
  475.         SubTime(&Remain,&Lead);
  476.     }
  477.     else
  478.     {
  479.             /* Choose the right rate accounting time. */
  480.  
  481.         SelectTime(ChosenEntry,ChosenPattern,&Now);
  482.  
  483.             /* Enter the first rate. */
  484.  
  485.         Cost = PayPerUnit[DT_FIRST_UNIT];
  486.  
  487.         Remain.tv_secs    = SecPerUnit[DT_FIRST_UNIT] / 10000;
  488.         Remain.tv_micro    = (SecPerUnit[DT_FIRST_UNIT] % 10000) * 100;
  489.     }
  490.  
  491.         /* Store the cost */
  492.  
  493.     ObtainSemaphore(&AccountantSemaphore);
  494.     AccountantCost = Cost;
  495.     ReleaseSemaphore(&AccountantSemaphore);
  496.  
  497.         /* That's it; "Remain" now holds the time to wait before */
  498.         /* the first unit elapses. Careful here, we must not */
  499.         /* launch a `zero' time wait request. */
  500.  
  501.     if(Remain.tv_secs > 0 || Remain.tv_micro > 0)
  502.     {
  503.         AccountantMsg Message;
  504.  
  505.         memset(&Message,0,sizeof(Message));
  506.  
  507.         Message.Message.mn_Length    = sizeof(Message);
  508.         Message.Type                = ACMSG_Start;
  509.         Message.Time                = Remain;
  510.  
  511.         SendMessageGetReply(AccountantPort,(struct Message *)&Message);
  512.     }
  513.  
  514.     UnlockActiveEntry(GlobalPhoneHandle);
  515.     UnlockActivePattern();
  516. }
  517.